From 65dd9da44a0a9d5ee211e36c6a779a682469e106 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 12 Feb 2016 07:59:06 +0100 Subject: [PATCH] css: Add support for sums to calc() This requires adding code to do math on number values: gtk_css_number_value_multiply() and gtk_css_number_value_try_add() were added to achieve that. Some tests are included. --- gtk/gtkcsscalcvalue.c | 282 +++++++++++++++++++++++++++---- gtk/gtkcsscalcvalueprivate.h | 3 + gtk/gtkcssdimensionvalue.c | 21 ++- gtk/gtkcssnumbervalue.c | 36 ++++ gtk/gtkcssnumbervalueprivate.h | 10 ++ testsuite/css/parser/Makefile.am | 1 + testsuite/css/parser/calc.css | 7 + 7 files changed, 324 insertions(+), 36 deletions(-) create mode 100644 testsuite/css/parser/calc.css diff --git a/gtk/gtkcsscalcvalue.c b/gtk/gtkcsscalcvalue.c index 51839c0514..374b23313b 100644 --- a/gtk/gtkcsscalcvalue.c +++ b/gtk/gtkcsscalcvalue.c @@ -19,48 +19,127 @@ #include "gtkcsscalcvalueprivate.h" -#include "gtkcssenumvalueprivate.h" -#include "gtkcssinitialvalueprivate.h" -#include "gtkstylepropertyprivate.h" - -#include "fallback-c89.c" +#include struct _GtkCssValue { GTK_CSS_VALUE_BASE - GtkCssValue *term; + gsize n_terms; + GtkCssValue * terms[1]; }; +static gsize +gtk_css_value_calc_get_size (gsize n_terms) +{ + g_assert (n_terms > 0); + + return sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (n_terms - 1); +} static void gtk_css_value_calc_free (GtkCssValue *value) { - _gtk_css_value_unref (value->term); - g_slice_free (GtkCssValue, value); + gsize i; + + for (i = 0; i < value->n_terms; i++) + { + _gtk_css_value_unref (value->terms[i]); + } + + g_slice_free1 (gtk_css_value_calc_get_size (value->n_terms), value); } -static GtkCssValue *gtk_css_calc_value_new (GtkCssValue *term); +static GtkCssValue *gtk_css_calc_value_new (gsize n_terms); + +static GtkCssValue * +gtk_css_value_new_from_array (GPtrArray *array) +{ + GtkCssValue *result; + + if (array->len > 1) + { + result = gtk_css_calc_value_new (array->len); + memcpy (result->terms, array->pdata, array->len * sizeof (GtkCssValue *)); + } + else + { + result = g_ptr_array_index (array, 0); + } + + g_ptr_array_free (array, TRUE); + + return result; +} + +static void +gtk_css_calc_array_add (GPtrArray *array, GtkCssValue *value) +{ + gsize i; + + for (i = 0; i < array->len; i++) + { + GtkCssValue *sum = gtk_css_number_value_try_add (g_ptr_array_index (array, i), value); + + if (sum) + { + g_ptr_array_index (array, i) = sum; + _gtk_css_value_unref (value); + return; + } + } + + g_ptr_array_add (array, value); +} static GtkCssValue * -gtk_css_value_calc_compute (GtkCssValue *calc, +gtk_css_value_calc_compute (GtkCssValue *value, guint property_id, GtkStyleProviderPrivate *provider, GtkCssStyle *style, GtkCssStyle *parent_style) { - GtkCssValue *computed_term; + GtkCssValue *result; + GPtrArray *array; + gboolean changed; + gsize i; - computed_term = _gtk_css_value_compute (calc->term, property_id, provider, style, parent_style); - if (computed_term == calc->term) - return _gtk_css_value_ref (calc); + array = g_ptr_array_new (); + for (i = 0; i < value->n_terms; i++) + { + GtkCssValue *computed = _gtk_css_value_compute (value->terms[i], property_id, provider, style, parent_style); + changed |= computed != value->terms[i]; + gtk_css_calc_array_add (array, computed); + } + + if (changed) + { + result = gtk_css_value_new_from_array (array); + } + else + { + g_ptr_array_set_free_func (array, (GDestroyNotify) _gtk_css_value_unref); + g_ptr_array_free (array, TRUE); + result = _gtk_css_value_ref (value); + } - return gtk_css_calc_value_new (computed_term); + return result; } static gboolean -gtk_css_value_calc_equal (const GtkCssValue *calc1, - const GtkCssValue *calc2) +gtk_css_value_calc_equal (const GtkCssValue *value1, + const GtkCssValue *value2) { - return _gtk_css_value_equal (calc1->term, calc2->term); + gsize i; + + if (value1->n_terms != value2->n_terms) + return FALSE; + + for (i = 0; i < value1->n_terms; i++) + { + if (!_gtk_css_value_equal (value1->terms[i], value2->terms[i])) + return FALSE; + } + + return TRUE; } static GtkCssValue * @@ -73,31 +152,87 @@ gtk_css_value_calc_transition (GtkCssValue *start, } static void -gtk_css_value_calc_print (const GtkCssValue *calc, +gtk_css_value_calc_print (const GtkCssValue *value, GString *string) { + gsize i; + g_string_append (string, "calc("); - _gtk_css_value_print (calc->term, string); + _gtk_css_value_print (value->terms[0], string); + + for (i = 1; i < value->n_terms; i++) + { + g_string_append (string, " + "); + _gtk_css_value_print (value->terms[i], string); + } g_string_append (string, ")"); } -double +static double gtk_css_value_calc_get (const GtkCssValue *value, double one_hundred_percent) { - return _gtk_css_number_value_get (value->term, one_hundred_percent); + double result = 0.0; + gsize i; + + for (i = 0; i < value->n_terms; i++) + { + result += _gtk_css_number_value_get (value->terms[i], one_hundred_percent); + } + + return result; } -GtkCssDimension +static GtkCssDimension gtk_css_value_calc_get_dimension (const GtkCssValue *value) { - return gtk_css_number_value_get_dimension (value->term); + GtkCssDimension dimension = GTK_CSS_DIMENSION_PERCENTAGE; + gsize i; + + for (i = 0; i < value->n_terms && dimension == GTK_CSS_DIMENSION_PERCENTAGE; i++) + { + dimension = gtk_css_number_value_get_dimension (value->terms[i]); + } + + return dimension; } -gboolean +static gboolean gtk_css_value_calc_has_percent (const GtkCssValue *value) { - return gtk_css_number_value_has_percent (value->term); + gsize i; + + for (i = 0; i < value->n_terms; i++) + { + if (gtk_css_number_value_has_percent (value->terms[i])) + return TRUE; + } + + return FALSE; +} + +static GtkCssValue * +gtk_css_value_calc_multiply (const GtkCssValue *value, + double factor) +{ + GtkCssValue *result; + gsize i; + + result = gtk_css_calc_value_new (value->n_terms); + + for (i = 0; i < value->n_terms; i++) + { + result->terms[i] = gtk_css_number_value_multiply (value->terms[i], factor); + } + + return result; +} + +static GtkCssValue * +gtk_css_value_calc_try_add (const GtkCssValue *value1, + const GtkCssValue *value2) +{ + return NULL; } static const GtkCssNumberValueClass GTK_CSS_VALUE_CALC = { @@ -111,15 +246,92 @@ static const GtkCssNumberValueClass GTK_CSS_VALUE_CALC = { gtk_css_value_calc_get, gtk_css_value_calc_get_dimension, gtk_css_value_calc_has_percent, + gtk_css_value_calc_multiply, + gtk_css_value_calc_try_add }; static GtkCssValue * -gtk_css_calc_value_new (GtkCssValue *term) +gtk_css_calc_value_new (gsize n_terms) { GtkCssValue *result; - result = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_CALC.value_class); - result->term = term; + result = _gtk_css_value_alloc (>K_CSS_VALUE_CALC.value_class, + gtk_css_value_calc_get_size (n_terms)); + result->n_terms = n_terms; + + return result; +} + +GtkCssValue * +gtk_css_calc_value_new_sum (GtkCssValue *value1, + GtkCssValue *value2) +{ + GPtrArray *array; + gsize i; + + array = g_ptr_array_new (); + + if (value1->class == >K_CSS_VALUE_CALC.value_class) + { + for (i = 0; i < value1->n_terms; i++) + { + gtk_css_calc_array_add (array, _gtk_css_value_ref (value1->terms[i])); + } + } + else + { + gtk_css_calc_array_add (array, _gtk_css_value_ref (value1)); + } + + if (value2->class == >K_CSS_VALUE_CALC.value_class) + { + for (i = 0; i < value2->n_terms; i++) + { + gtk_css_calc_array_add (array, _gtk_css_value_ref (value2->terms[i])); + } + } + else + { + gtk_css_calc_array_add (array, _gtk_css_value_ref (value2)); + } + + return gtk_css_value_new_from_array (array); +} + +GtkCssValue * +gtk_css_calc_value_parse_sum (GtkCssParser *parser, + GtkCssNumberParseFlags flags) +{ + GtkCssValue *result; + + result = _gtk_css_number_value_parse (parser, flags); + if (result == NULL) + return NULL; + + while (_gtk_css_parser_begins_with (parser, '+') || _gtk_css_parser_begins_with (parser, '-')) + { + GtkCssValue *next, *temp; + + if (_gtk_css_parser_try (parser, "+", TRUE)) + { + next = _gtk_css_number_value_parse (parser, flags); + } + else if (_gtk_css_parser_try (parser, "-", TRUE)) + { + temp = _gtk_css_number_value_parse (parser, flags); + next = gtk_css_number_value_multiply (temp, -1); + _gtk_css_value_unref (temp); + } + else + { + g_assert_not_reached (); + } + + temp = gtk_css_number_value_add (result, next); + _gtk_css_value_unref (result); + _gtk_css_value_unref (next); + result = temp; + } return result; } @@ -128,7 +340,7 @@ GtkCssValue * gtk_css_calc_value_parse (GtkCssParser *parser, GtkCssNumberParseFlags flags) { - GtkCssValue *term; + GtkCssValue *value; if (!_gtk_css_parser_try (parser, "calc(", TRUE)) { @@ -136,17 +348,17 @@ gtk_css_calc_value_parse (GtkCssParser *parser, return NULL; } - term = _gtk_css_number_value_parse (parser, flags); - if (term == NULL) + value = gtk_css_calc_value_parse_sum (parser, flags); + if (value == NULL) return NULL; if (!_gtk_css_parser_try (parser, ")", TRUE)) { - _gtk_css_value_unref (term); - _gtk_css_parser_error (parser, "Expected ')' for calc() statement"); + _gtk_css_value_unref (value); + _gtk_css_parser_error (parser, "Expected ')' after calc() statement"); return NULL; } - return gtk_css_calc_value_new (term); + return value; } diff --git a/gtk/gtkcsscalcvalueprivate.h b/gtk/gtkcsscalcvalueprivate.h index 247d4c8a2a..2d113150ff 100644 --- a/gtk/gtkcsscalcvalueprivate.h +++ b/gtk/gtkcsscalcvalueprivate.h @@ -22,6 +22,9 @@ G_BEGIN_DECLS +GtkCssValue * gtk_css_calc_value_new_sum (GtkCssValue *value1, + GtkCssValue *value2); + GtkCssValue * gtk_css_calc_value_parse (GtkCssParser *parser, GtkCssNumberParseFlags flags); diff --git a/gtk/gtkcssdimensionvalue.c b/gtk/gtkcssdimensionvalue.c index edf8a6fc75..f1a4d90bfb 100644 --- a/gtk/gtkcssdimensionvalue.c +++ b/gtk/gtkcssdimensionvalue.c @@ -241,6 +241,23 @@ gtk_css_value_dimension_has_percent (const GtkCssValue *value) return gtk_css_unit_get_dimension (value->unit) == GTK_CSS_DIMENSION_PERCENTAGE; } +static GtkCssValue * +gtk_css_value_dimension_multiply (const GtkCssValue *value, + double factor) +{ + return gtk_css_dimension_value_new (value->value * factor, value->unit); +} + +static GtkCssValue * +gtk_css_value_dimension_try_add (const GtkCssValue *value1, + const GtkCssValue *value2) +{ + if (value1->unit != value2->unit) + return NULL; + + return gtk_css_dimension_value_new (value1->value + value2->value, value1->unit); +} + static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = { { gtk_css_value_dimension_free, @@ -251,7 +268,9 @@ static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = { }, gtk_css_value_dimension_get, gtk_css_value_dimension_get_dimension, - gtk_css_value_dimension_has_percent + gtk_css_value_dimension_has_percent, + gtk_css_value_dimension_multiply, + gtk_css_value_dimension_try_add }; GtkCssValue * diff --git a/gtk/gtkcssnumbervalue.c b/gtk/gtkcssnumbervalue.c index 12508db130..b7892c7603 100644 --- a/gtk/gtkcssnumbervalue.c +++ b/gtk/gtkcssnumbervalue.c @@ -42,6 +42,42 @@ gtk_css_number_value_has_percent (const GtkCssValue *value) return number_value_class->has_percent (value); } +GtkCssValue * +gtk_css_number_value_multiply (const GtkCssValue *value, + double factor) +{ + GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class; + + return number_value_class->multiply (value, factor); +} + +GtkCssValue * +gtk_css_number_value_add (GtkCssValue *value1, + GtkCssValue *value2) +{ + GtkCssValue *sum; + + sum = gtk_css_number_value_try_add (value1, value2); + if (sum == NULL) + sum = gtk_css_calc_value_new_sum (value1, value2); + + return sum; +} + +GtkCssValue * +gtk_css_number_value_try_add (const GtkCssValue *value1, + const GtkCssValue *value2) +{ + GtkCssNumberValueClass *number_value_class; + + if (value1->class != value2->class) + return NULL; + + number_value_class = (GtkCssNumberValueClass *) value1->class; + + return number_value_class->try_add (value1, value2); +} + GtkCssValue * _gtk_css_number_value_new (double value, GtkCssUnit unit) diff --git a/gtk/gtkcssnumbervalueprivate.h b/gtk/gtkcssnumbervalueprivate.h index e45eeaaaa8..8e77aaa9f0 100644 --- a/gtk/gtkcssnumbervalueprivate.h +++ b/gtk/gtkcssnumbervalueprivate.h @@ -45,6 +45,10 @@ struct _GtkCssNumberValueClass { double one_hundred_percent); GtkCssDimension (* get_dimension) (const GtkCssValue *value); gboolean (* has_percent) (const GtkCssValue *value); + GtkCssValue * (* multiply) (const GtkCssValue *value, + double factor); + GtkCssValue * (* try_add) (const GtkCssValue *value1, + const GtkCssValue *value2); }; GtkCssValue * _gtk_css_number_value_new (double value, @@ -55,6 +59,12 @@ GtkCssValue * _gtk_css_number_value_parse (GtkCssParser *par GtkCssDimension gtk_css_number_value_get_dimension (const GtkCssValue *value); gboolean gtk_css_number_value_has_percent (const GtkCssValue *value); +GtkCssValue * gtk_css_number_value_multiply (const GtkCssValue *value, + double factor); +GtkCssValue * gtk_css_number_value_add (GtkCssValue *value1, + GtkCssValue *value2); +GtkCssValue * gtk_css_number_value_try_add (const GtkCssValue *value1, + const GtkCssValue *value2); double _gtk_css_number_value_get (const GtkCssValue *number, double one_hundred_percent); diff --git a/testsuite/css/parser/Makefile.am b/testsuite/css/parser/Makefile.am index 28fa71fcf3..0cc0cce1b8 100644 --- a/testsuite/css/parser/Makefile.am +++ b/testsuite/css/parser/Makefile.am @@ -226,6 +226,7 @@ test_data = \ border-width.ref.css \ box-shadow.css \ box-shadow.ref.css \ + calc.css \ calc-simple.css \ calc-simple.ref.css \ close-at-end-of-file.css \ diff --git a/testsuite/css/parser/calc.css b/testsuite/css/parser/calc.css new file mode 100644 index 0000000000..2c22095c7c --- /dev/null +++ b/testsuite/css/parser/calc.css @@ -0,0 +1,7 @@ +a { + margin-left: calc(3px + 1em); +} + +a { + transition-duration: calc(1s - 100ms + -100ms); +} -- 2.30.2